/*
 * Copyright (c) 2001-2023 cnstrong.cn Corporation Limited. All rights reserved.
 * This software is the confidential and proprietary information of cnstrong.cn Company.
 * ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the license agreement you entered into with cnstrong.cn.
 */
package com.tree.config.repository.impl;

import com.tree.config.annotation.CfgBeanCode;
import com.tree.config.base.WeTreeConstants;
import com.tree.config.common.ConfigCovertTool;
import com.tree.config.configuration.codis.WeTreeCodisProperties;
import com.tree.config.enums.EnvEnum;
import com.tree.config.mapper.MicrodomainProjectConfigDefineMapper;
import com.tree.config.mapper.MicrodomainProjectConfigValueMapper;
import com.tree.config.repository.dataobject.ProjectConfigDefineDO;
import com.tree.config.repository.dataobject.ProjectConfigValueDO;
import com.tree.config.repository.po.ProjectConfigDefinePO;
import com.tree.config.repository.po.ProjectConfigValuePO;
import com.tree.config.utils.DictCacheKeyUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * WeTreeRepositoryImpl
 *
 * @author Braveheart
 * @version V1.0
 * @since 2023-12-28
 */
@Slf4j
@Component
public class WeTreeRepositoryImpl {
    @Resource
    private MicrodomainProjectConfigDefineMapper microdomainProjectConfigDefineMapper;
    @Resource
    private MicrodomainProjectConfigValueMapper microdomainProjectConfigValueMapper;
    @Resource
    private CodisAgent weTreeCodisAgent;
    @Resource
    private WeTreeCodisProperties weTreeCodisProperties;

    /**
     * 系统环境
     * @see com.tree.config.enums.EnvEnum
     */
    @Value("${w.tree.env:}")
    private Integer systemEnv;


    private <T> String getCodisKey(Object bizId, Class<T> clz) {
        Optional<CfgBeanCode> optionalCfgBeanCode  = Optional.ofNullable(clz.getDeclaredAnnotation(CfgBeanCode.class));
        String projectCode = optionalCfgBeanCode.map(CfgBeanCode::projectCode).orElse(null);
        String parentCode = optionalCfgBeanCode.map(CfgBeanCode::parentCode).orElse(null);
        String currCode = optionalCfgBeanCode.map(CfgBeanCode::currCode).orElse(null);
        return WeTreeConstants.CODIS_VALUE_NAMESPACE + projectCode + ":" + parentCode + ":" + currCode + ":" + bizId;
    }

    @Override
    public String getDictCommon(String projectCode, String parentCfgCode, String currCfgCode, Object bizId) {
        Objects.requireNonNull(projectCode, "工程code不能为空");
        Objects.requireNonNull(parentCfgCode, "一级code不能为空");
        Objects.requireNonNull(currCfgCode, "二级code不能为空");
        //1. 查询缓存
        //1.1 构建缓存key
        String redisKey = DictCacheKeyUtils.getCacheKey(projectCode, parentCfgCode, currCfgCode, bizId, systemEnv);
        //1.2 查询缓存
        String cacheVal = this.findCache(redisKey);
        //1.3 有数据直接返回
        if (StringUtils.isNotBlank(cacheVal)) {
            return cacheVal;
        }
        //2. 开启查询db或缓存无数据查询db
        //2.1 查询db
        List<ProjectConfigValueDO> configValueDOList = listConfigValue(projectCode, parentCfgCode, currCfgCode, bizId);
        List<ProjectConfigDefineDO> configDefineDOList = listConfigDefine(projectCode, parentCfgCode, currCfgCode);
        String valJson = null;
        if (CollectionUtils.isNotEmpty(configValueDOList)) {
            valJson = ConfigCovertTool.covertToJson(configDefineDOList, configValueDOList);
        }
        this.cache(redisKey, valJson);
        return valJson;
    }

    @Override
    public String findCache(String codisKey) {
        if (!weTreeCodisProperties.isEnable()) {
            return null;
        }
        return weTreeCodisAgent.get(codisKey);
    }

    @Override
    public <T> String findCache(String bizId, Class<T> clz) {
        return findCache(getCodisKey(bizId, clz));
    }

    @Override
    public void cache(String codisKey, String cacheVal) {
        if (!weTreeCodisProperties.isEnable()) {
            return;
        }
        if (StringUtils.isBlank(cacheVal)) {
            weTreeCodisAgent.setex(codisKey, WeTreeConstants.PIERCE_THROUGH_EXPIRE, WeTreeConstants.CODIS_VALUE_PIERCE_THROUGH);
            return;
        }
        weTreeCodisAgent.setex(codisKey, WeTreeConstants.CODIS_EXPIRE, cacheVal);
    }

    @Override
    public <T> void cache(String bizId, Class<T> clz, String cacheVal) {
        this.cache(getCodisKey(bizId, clz), cacheVal);
    }

    @Override
    public void clearCache(String codisKey) {
        if (!weTreeCodisProperties.isEnable()) {
            return;
        }
        weTreeCodisAgent.del(codisKey);
    }

    @Override
    public <T> void clearCache(String bizId, Class<T> clz) {
        clearCache(getCodisKey(bizId, clz));
    }

    @Override
    public List<ProjectConfigDefineDO> listConfigDefine(ProjectConfigDefinePO configDefinePO) {
        Objects.requireNonNull(configDefinePO, "入参不能为空");
        Objects.requireNonNull(configDefinePO.getProjectCode(), "工程code不能为空");
        return microdomainProjectConfigDefineMapper.selectConfigDefine(configDefinePO);
    }

    @Override
    public List<ProjectConfigValueDO> listConfigValue(ProjectConfigValuePO configValuePO) {
        Objects.requireNonNull(configValuePO, "入参不能为空");
        Objects.requireNonNull(configValuePO.getProjectCode(), "工程code不能为空");
        return microdomainProjectConfigValueMapper.selectConfigValues(configValuePO);
    }

    /**
     * 查询配置定义
     */
    private List<ProjectConfigDefineDO> listConfigDefine(String projectCode, String parentCode, String currCode) {
        // 查询配置定义
        ProjectConfigDefinePO projectConfigDefinePO = new ProjectConfigDefinePO();
        projectConfigDefinePO.setProjectCode(projectCode);
        projectConfigDefinePO.setCfgCode1(parentCode);
        projectConfigDefinePO.setCfgCode2(currCode);
        return this.listConfigDefine(projectConfigDefinePO);
    }

    /**
     * 查询配置值
     */
    private List<ProjectConfigValueDO> listConfigValue(String projectCode, String parentCfgCode, String currCfgCode,
        Object bizId) {
        ProjectConfigValuePO configValuePO = new ProjectConfigValuePO();
        configValuePO.setProjectCode(projectCode);
        configValuePO.setBizId(bizId + "");
        configValuePO.setCfgCode1(parentCfgCode);
        configValuePO.setCfgCode2(currCfgCode);
        configValuePO.setEnvList(Arrays.asList(EnvEnum.ALL.getType(), systemEnv));
        configValuePO.setIsValid(1);
        return this.listConfigValue(configValuePO);
    }
}
